package cn.fastloader.web.controller;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.fastloader.config.MinioUtil;
import cn.fastloader.config.SysUploadService;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import cn.fastloader.util.ServletUtils;
import cn.fastloader.web.model.ApiResult;
import cn.fastloader.web.model.QueryInfo;
import cn.fastloader.web.model.TChunkInfo;
import cn.fastloader.web.model.TFileInfo;
import cn.fastloader.web.model.TFileInfoVO;
import cn.fastloader.web.model.UploadResult;
import cn.fastloader.web.service.ChunkService;
import cn.fastloader.web.service.FileInfoService;

/**
 * 上传下载文件
 * @author 洋葱骑士
 * 源码地址：https://gitee.com/luckytuan/fast-loader
 *
 */
@Slf4j
@RestController
@RequestMapping("/uploader")
public class FileBigController {


    @Value("${minio.bucket}")
    private String bucketName;
    
    @Resource
    private FileInfoService fileInfoService;
    
    @Resource
    private ChunkService chunkService;
    @Resource
    protected SysUploadService service;
    @Autowired
    RedisTemplate redisTemplate;
    private final Logger logger = LoggerFactory.getLogger(FileBigController.class);
    
    /**
     * 上传文件块
     * @param chunk
     * @return
     */
    @PostMapping("/chunk")
    public String uploadChunk(TChunkInfo chunk) {
    	String apiRlt = "200";
    	
        MultipartFile file = chunk.getUpfile();
        logger.info("file originName: {}, chunkNumber: {}", file.getOriginalFilename(), chunk.getChunkNumber());

        try {
            //判断文件是否是多片
            Integer totalChunks = chunk.getTotalChunks();
            String identifier = chunk.getIdentifier();
            LocalDate localDate = LocalDate.now();
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_");
            String format = localDate.format(dateTimeFormatter);
            // 设置文件名路径
            String fileName = format+chunk.getFilename();

            // 如果非多片，直接上传
            if (totalChunks==1){
                return MinioUtil.upload(bucketName, chunk.getUpfile(), fileName);
            }


            boolean register = service.createTempBucket(identifier);
            MinioUtil.upload(identifier, chunk.getUpfile(), String.valueOf(chunk.getChunkNumber()));
            // 如果上传临时桶成功，redis+1
            if (register){
                try{
                    redisTemplate.opsForValue().increment(identifier);
                }catch (Exception e){
                    throw new RuntimeException(e.getMessage());
//                throw new BusinessException("redis递增桶的片出错！",e.getMessage()).setCause(e);
                }
                // 如果redis中分片大小等于桶的总分片大小，则合并分片

            }
            if(chunkService.saveChunk(chunk) < 0) apiRlt = "415";
            
        } catch (Exception e) {
            e.printStackTrace();
            apiRlt = "415";
        }
        return apiRlt;
    }

    @GetMapping("/chunk")
    public UploadResult checkChunk(TChunkInfo chunk, HttpServletResponse response) {
    	UploadResult ur = new UploadResult();
    	
    	//默认返回其他状态码，前端不进去checkChunkUploadedByResponse函数，正常走标准上传
    	response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);

        String identifier = chunk.getIdentifier();
        Integer totalChunks = chunk.getTotalChunks();
        Integer chunkNumber = chunk.getChunkNumber();
        Map<String, Object> data = new HashMap<>(1);
        Boolean exists = redisTemplate.hasKey(identifier);
        // 判断redis中是否还有此文件MD5
        if (exists){
            Map<String,Object> map = new HashMap<>(2);
            int identifiers = Integer.parseInt(redisTemplate.opsForValue().get(identifier)+"");
            int[] uploadedChunks = new int[identifiers];
            for (int i = 1; i <= identifiers; i++) {
                uploadedChunks[i-1]=i;
            }
            ur.setSkipUpload(true);
            response.setStatus(HttpServletResponse.SC_OK);
            map.put("uploadedChunks",uploadedChunks);
            ur.setMessage("完整文件已存在，直接跳过上传，实现秒传");
        }else {
            // 判断是否是多片，如果是多片则创建临时桶
            if (totalChunks>1){
                service.createTempBucket(chunk.getIdentifier());
            }
            redisTemplate.opsForValue().set("file:"+identifier,chunk);
        }
    	
    	//如果完整文件不存在，则去数据库判断当前哪些文件块已经上传过了，把结果告诉前端，跳过这些文件块的上传，实现断点续传
    	ArrayList<Integer> list = chunkService.checkChunk(chunk);
    	if (list !=null && list.size() > 0) {
    		ur.setSkipUpload(false);
    		ur.setUploadedChunks(list);
    		response.setStatus(HttpServletResponse.SC_OK);
    		ur.setMessage("部分文件块已存在，继续上传剩余文件块，实现断点续传");
    		return ur;
        }
        return ur;
    }

    @PostMapping("/mergeFile")
    public String mergeFile(@RequestBody TFileInfoVO fileInfoVO){
    	
    	String rlt = "FALURE";
    	
    	//前端组件参数转换为model对象
    	TFileInfo fileInfo = new TFileInfo();
    	fileInfo.setFilename(fileInfoVO.getName());
    	fileInfo.setIdentifier(fileInfoVO.getUniqueIdentifier());
//    	fileInfo.setId(fileInfoVO.getId());
    	fileInfo.setTotalSize(fileInfoVO.getSize());
    	
    	//进行文件的合并操作
        String filename = fileInfo.getFilename();
        String file = bucketName + filename;
        
        fileInfo.setLocation(file);

        String identifier = fileInfo.getIdentifier();
        TChunkInfo chunkInfo = JSONObject.parseObject(JSONObject.toJSONString(redisTemplate.opsForValue().get("file:"+identifier)),TChunkInfo.class);

        if(chunkInfo.getTotalChunks()>1) {
//            if (redisTemplate.opsForValue().get(identifier).toString().equals(String.valueOf(chunkInfo.getTotalChunks()))) {
                service.mergeTempBucket(identifier, filename);
                log.info("fileInfo.getIdentifier():{}", identifier);
//            }
        }

        //文件合并成功后，保存记录至数据库
        if(fileInfoService.addFileInfo(fileInfo) > 0) rlt = "SUCCESS";
        redisTemplate.delete("file:"+identifier);
        return rlt;
    }
    
    /**
     * 查询列表
     *
     * @return ApiResult
     */
    @RequestMapping(value = "/selectFileList", method = RequestMethod.POST)
    public ApiResult selectFileList(@RequestBody QueryInfo  query){  
        PageHelper.startPage(query.getPageIndex(), query.getPageSize()); 
        List<TFileInfo> list =  fileInfoService.selectFileList(query);		
        PageInfo<TFileInfo> pageResult = new PageInfo<>(list);				
        return ApiResult.success(pageResult);
    }
  
    /**
     * 下载文件
     * @param req
     * @param resp
     */
    @RequestMapping(value = "/download", method = RequestMethod.GET)
    public void download(HttpServletRequest req, HttpServletResponse resp){
    	String location = req.getParameter("location"); 
    	String fileName = req.getParameter("filename");
    	BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        OutputStream fos = null;
        try {
            bis = new BufferedInputStream(new FileInputStream(location));
            fos = resp.getOutputStream();
            bos = new BufferedOutputStream(fos);
            ServletUtils.setFileDownloadHeader(req, resp, fileName);
            int byteRead = 0;
            byte[] buffer = new byte[8192];
            while ((byteRead = bis.read(buffer, 0, 8192)) != -1) {
                bos.write(buffer, 0, byteRead);
            }
        } catch (Exception e) {
        	e.printStackTrace();
        } finally {
            try {
                bos.flush();
                bis.close();
                fos.close();
                bos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    /**
     * 删除
     */
    @RequestMapping(value = "/deleteFile", method = RequestMethod.POST)
    public ApiResult deleteFile(@RequestBody TFileInfo tFileInfo ){     	        
    	int result = fileInfoService.deleteFile(tFileInfo);		
        return ApiResult.success(result);
    }
}
